<!DOCTYPE html>
<html>
<head>
<title>Optical Filter Diagram</title>
</head>
<body>
<canvas id="physicsCanvas" width="900" height="400"></canvas>
<script>
const canvas = document.getElementById('physicsCanvas');
const ctx = canvas.getContext('2d');

// Style settings
const defaultFont = "italic 20px 'Times New Roman'";
const subscriptFont = "italic 14px 'Times New Roman'";
const defaultColor = 'black';

ctx.strokeStyle = defaultColor;
ctx.fillStyle = defaultColor;
ctx.lineWidth = 2;
ctx.font = defaultFont;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';

// --- Helper Functions ---

/**
 * Draws an arrow on the canvas (for axes).
 * @param {number} fromX - Starting x-coordinate.
 * @param {number} fromY - Starting y-coordinate.
 * @param {number} toX - Ending x-coordinate.
 * @param {number} toY - Ending y-coordinate.
 * @param {number} headLength - Length of the arrowhead.
 */
function drawAxisArrow(fromX, fromY, toX, toY, headLength = 10) {
    const dx = toX - fromX;
    const dy = toY - fromY;
    const angle = Math.atan2(dy, dx);
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(fromX, fromY);
    ctx.lineTo(toX, toY);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(toX, toY);
    ctx.lineTo(toX - headLength * Math.cos(angle - Math.PI / 6), toY - headLength * Math.sin(angle - Math.PI / 6));
    ctx.moveTo(toX, toY);
    ctx.lineTo(toX - headLength * Math.cos(angle + Math.PI / 6), toY - headLength * Math.sin(angle + Math.PI / 6));
    ctx.stroke();
    ctx.restore();
}

/**
 * Draws a double-headed arrow.
 * @param {number} x1 - Starting x-coordinate.
 * @param {number} y1 - Starting y-coordinate.
 * @param {number} x2 - Ending x-coordinate.
 * @param {number} y2 - Ending y-coordinate.
 * @param {number} headLength - Length of the arrowhead.
 */
function drawDoubleArrow(x1, y1, x2, y2, headLength = 8) {
    ctx.save();
    ctx.lineWidth = 1.5;
    // Main line
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.stroke();
    
    // Head at (x2, y2)
    let angle = Math.atan2(y2 - y1, x2 - x1);
    ctx.beginPath();
    ctx.moveTo(x2, y2);
    ctx.lineTo(x2 - headLength * Math.cos(angle - Math.PI / 6), y2 - headLength * Math.sin(angle - Math.PI / 6));
    ctx.moveTo(x2, y2);
    ctx.lineTo(x2 - headLength * Math.cos(angle + Math.PI / 6), y2 - headLength * Math.sin(angle + Math.PI / 6));
    ctx.stroke();

    // Head at (x1, y1)
    angle = Math.atan2(y1 - y2, x1 - x2);
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x1 - headLength * Math.cos(angle - Math.PI / 6), y1 - headLength * Math.sin(angle - Math.PI / 6));
    ctx.moveTo(x1, y1);
    ctx.lineTo(x1 - headLength * Math.cos(angle + Math.PI / 6), y1 - headLength * Math.sin(angle + Math.PI / 6));
    ctx.stroke();
    ctx.restore();
}

/**
 * Draws a bell-shaped (Gaussian-like) curve.
 * @param {number} cx - Center x-coordinate of the peak.
 * @param {number} yBase - y-coordinate of the curve's base.
 * @param {number} height - Height of the peak from the base.
 * @param {number} width - Half-width at the base of the curve.
 * @param {string} style - 'solid', 'dashed'.
 */
function drawBellCurve(cx, yBase, height, width, style) {
    const yPeak = yBase - height;
    const xStart = cx - width;
    const xEnd = cx + width;
    const controlOffsetRatio = 0.7;

    ctx.save();
    ctx.lineWidth = 2;
    if (style === 'dashed') {
        ctx.setLineDash([6, 4]);
    }
    
    ctx.beginPath();
    ctx.moveTo(xStart, yBase);
    ctx.bezierCurveTo(cx - width * controlOffsetRatio, yPeak, cx + width * controlOffsetRatio, yPeak, xEnd, yBase);
    ctx.stroke();
    
    ctx.restore();
}

/**
 * Draws a label with an optional subscript.
 * @param {string} baseText - The main part of the label.
 * @param {string} subText - The subscript text.
 * @param {number} x - The x-coordinate for the label's horizontal center.
 * @param {number} y - The y-coordinate for the label.
 * @param {string} align - 'center' or 'left'.
 */
function drawSubscriptLabel(baseText, subText, x, y, align = 'center') {
    ctx.save();
    ctx.font = defaultFont;
    const baseWidth = ctx.measureText(baseText).width;
    
    ctx.font = subscriptFont;
    const subWidth = ctx.measureText(subText).width;
    const totalWidth = baseWidth + subWidth;
    
    let startX = x;
    if (align === 'center') {
        startX = x - totalWidth / 2;
    }

    ctx.font = defaultFont;
    ctx.textAlign = 'left';
    ctx.fillText(baseText, startX, y);
    
    ctx.font = subscriptFont;
    ctx.fillText(subText, startX + baseWidth, y + 5);
    
    ctx.restore();
}

// --- Main Drawing ---

// 1. Draw the main light beam
const beamY = 280;
ctx.save();
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(40, beamY);
ctx.lineTo(860, beamY);
ctx.stroke();
// Add filled arrows to the beam
const drawBeamArrow = (cx, y) => {
    const headLength = 20;
    const halfWidth = 7;
    ctx.beginPath();
    ctx.moveTo(cx - headLength, y - halfWidth);
    ctx.lineTo(cx, y);
    ctx.lineTo(cx - headLength, y + halfWidth);
    ctx.fillStyle = defaultColor;
    ctx.fill();
}
drawBeamArrow(270, beamY);
drawBeamArrow(590, beamY);
drawBeamArrow(860, beamY);
ctx.restore();

// 2. Draw the filter
ctx.save();
ctx.fillStyle = '#cccccc';
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.fillRect(415, beamY - 45, 20, 90);
ctx.strokeRect(415, beamY - 45, 20, 90);
ctx.fillStyle = 'black';
ctx.textAlign = 'center';
ctx.fillText("F", 425, beamY - 60);
ctx.restore();

// 3. Draw the Left Graph (Before Filter)
const leftGraph = {
    ox: 100, oy: 220,
    w: 250, h: 140,
    cx: 210,
    peakH: 100,
    peakW: 80
};
// Axes
drawAxisArrow(leftGraph.ox, leftGraph.oy, leftGraph.ox + leftGraph.w, leftGraph.oy, 10); // x-axis
drawAxisArrow(leftGraph.ox, leftGraph.oy, leftGraph.ox, leftGraph.oy - leftGraph.h, 10); // y-axis
// Axis Labels
ctx.textAlign = 'center';
ctx.fillText("I(\u03BB)", leftGraph.ox + 30, leftGraph.oy - leftGraph.h + 10);
ctx.fillText("\u03BB", leftGraph.ox + leftGraph.w - 10, leftGraph.oy + 20);
// Curve
drawBellCurve(leftGraph.cx, leftGraph.oy, leftGraph.peakH, leftGraph.peakW, 'solid');
// Wavelength and width labels
drawSubscriptLabel("\u03BB", "0", leftGraph.cx, leftGraph.oy + 15);
const deltaLambdaY = leftGraph.oy - leftGraph.peakH * 0.6;
drawDoubleArrow(leftGraph.cx - 50, deltaLambdaY, leftGraph.cx + 50, deltaLambdaY);
drawSubscriptLabel("\u0394\u03BB", "", leftGraph.cx + 70, deltaLambdaY, 'left');

// 4. Draw the Right Graph (After Filter)
const rightGraph = {
    ox: 550, oy: 220,
    w: 280, h: 140
};
// Axes
drawAxisArrow(rightGraph.ox, rightGraph.oy, rightGraph.ox + rightGraph.w, rightGraph.oy, 10); // x-axis
drawAxisArrow(rightGraph.ox, rightGraph.oy, rightGraph.ox, rightGraph.oy - rightGraph.h, 10); // y-axis
// Axis Labels
ctx.textAlign = 'center';
ctx.fillText("I(\u03BB)", rightGraph.ox + 30, rightGraph.oy - rightGraph.h + 10);
ctx.fillText("\u03BB", rightGraph.ox + rightGraph.w - 10, rightGraph.oy + 20);

// Dashed original curve
const refCurve = {
    cx: rightGraph.ox + (leftGraph.cx - leftGraph.ox),
    peakH: leftGraph.peakH,
    peakW: leftGraph.peakW
};
drawBellCurve(refCurve.cx, rightGraph.oy, refCurve.peakH, refCurve.peakW, 'dashed');

// Solid filtered curve
const filteredCurve = {
    cx: refCurve.cx + 90,
    peakH: 70,
    peakW: 25
};
drawBellCurve(filteredCurve.cx, rightGraph.oy, filteredCurve.peakH, filteredCurve.peakW, 'solid');

// Dotted line for filtered peak
ctx.save();
ctx.setLineDash([2, 3]);
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(filteredCurve.cx, rightGraph.oy - filteredCurve.peakH);
ctx.lineTo(filteredCurve.cx, rightGraph.oy);
ctx.stroke();
ctx.restore();

// Wavelength and width labels for filtered light
drawSubscriptLabel("\u03BB", "f", filteredCurve.cx, rightGraph.oy + 15);
const deltaLambdafY = filteredCurve.oy - filteredCurve.peakH * 0.7;
drawDoubleArrow(filteredCurve.cx - 20, deltaLambdafY, filteredCurve.cx + 20, deltaLambdafY);
drawSubscriptLabel("\u0394\u03BB", "f", filteredCurve.cx + 45, deltaLambdafY, 'center');

</script>
</body>
</html>